﻿/******************************************************************************              
 * Author: 671643387@qq.com                                                                                                                                            
 * @MR.Luo                                                                                      
 *****************************************************************************/

#include <stdint.h>
#include <stdio.h>
#if defined(__WINDOWS__)
#include <crtdbg.h>
#endif
#include <signal.h>
#include <string>
#include <boost/program_options.hpp>

#include "Common.h"
#include "Worker.hpp"
using namespace dev;
namespace po = boost::program_options;

// 全局变量
std::string gsaddr;
int gsport;
int gsbufsize;
char* gscscMSG;
int gsFrameTime = 0;

bool srunning = true;

void signal_handler(int signal)
{
    if (signal == SIGINT)
    {
        srunning = false;
    }
}

struct _stHead
{
	uint16_t id;
	uint16_t size;
};

int main(int argc, char *argv[])
{
    po::options_description desc("allow options");
    desc.add_options()
        ("help,h", "show usage")
        ("address,a", po::value<std::string>()->default_value("127.0.0.1"), "connect address")
        ("port,p", po::value<int>()->default_value(8888), "connect port")
        ("threads,t", po::value<int>()->default_value(1), "threads")
        ("cpt,c", po::value<int>()->default_value(1), "connection per thread")
        ("ms,s", po::value<int>()->default_value(4096), "message size")
        ("ft,f", po::value<int>()->default_value(0), "frame time")
        ("message,m", po::value<std::string>()->default_value("10101010"), "message");

    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, desc), vm);

    if (vm.count("help"))
    {
        std::cout << desc << std::endl;
        return 0;
    }

    if (!(vm.count("ms") && vm.count("message")))
    {
        std::cout << desc << std::endl;
        return 0;
    }

    int workerCount = 0;
    int connPerWorker = 0;
    std::string msg;

    gsaddr = vm["address"].as<std::string>();
    gsport = vm["port"].as<int>();
    workerCount = vm["threads"].as<int>();
    connPerWorker = vm["cpt"].as<int>();
    gsbufsize = vm["ms"].as<int>();
    gsFrameTime = vm["ft"].as<int>();
    msg = vm["message"].as<std::string>();

#if defined(__WINDOWS__)
    //_CrtSetBreakAlloc(156);
    WSAData wsaData;
    ::WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif
	__DV_TRY
    {
        signal(SIGINT, signal_handler);

        
		size_t msgLen = msg.length();
        gscscMSG = new char[gsbufsize];
        memset(gscscMSG, 0, gsbufsize);
		struct _stHead* head = (struct _stHead*)gscscMSG;
		head->id = 1;
		head->size = gsbufsize;

		strncpy(gscscMSG + sizeof(struct _stHead), msg.c_str(),
			__DV_MAX(__DV_MIN(msgLen, gsbufsize - sizeof(struct _stHead) - 1) , 0));

        std::vector<WorkerPtr> workers;
        for (int x = 0; x < workerCount; ++x)
        {
            workers.push_back(WorkerPtr(new Worker(connPerWorker)));
        }

        for (std::vector<WorkerPtr>::iterator it = workers.begin(); 
            it != workers.end(); ++it)
        {
            (*it)->start();
        }

        int timeSec = 0;
        do
        {
            dev::base::Thread::sleep(1000);
            ++timeSec;
            
            printf("==============================\n");
            printf("total connections:        %d\n", gconns);
            printf("total packets:            %lld\n", gpackets);
            printf("total bytestransfered:    %lld %lld[K] %lld[M] \n", 
                gbytestransfered, gbytestransfered / 1024, gbytestransfered / 1048576);
            printf("packets per sec:          %lld\n", gpackets / timeSec);
            unsigned long long bytesPerSec = gbytestransfered / timeSec;
            printf("bytestransfered  per sec: %lld %lld[K] %lld[M] \n", 
                bytesPerSec, bytesPerSec / 1024, bytesPerSec / 1048576);

            if (gconns == 0)
                srunning = false;
        }while (srunning);

        printf("\n");

        for (std::vector<WorkerPtr>::iterator it = workers.begin();
            it != workers.end(); ++it)
        {
            (*it)->stop();
            (*it)->join();
        }
	}
	__DV_CATCH(std::exception& e)
	{
		__DV_DUMP(e);
	}

#if defined(__WINDOWS__)
    ::WSACleanup();
	_CrtDumpMemoryLeaks();
#endif
	return 0;
}
